#include "stdafx.h"
#include "CXSegment.h"
#include "CXSegmentSet.h"
#include "CXView.h"
#include "SegmentActions.h"
#include "Claxa.h"
#include "Resource.h"
#include <MyControls/Resource.h>
#include <MyControls/AsyncCall.h>
#include <MyControls/PropertyGroup.h>
#include <MyControls/StdActions.h>
#include <MyControls/Xml.h>
#include <MyControls/Clipboard.h>
#include <imglib/bmp.h>
#include <imglib/hexcode.h>
#include <outline/simplify3.h>
#include <outline/motion_line_estimator.h>

My::MaskEditor & GetMaskEditor()
{
	return theApp.GetFrame().GetView().GetMaskEditor();
}

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

SegmentData::SegmentData(int org, FootageSPtr const & f, CXSegment * Owner) :
	trm::segment(org, f),
	m_pOwner(Owner)
{
}

void SegmentData::frame_done(int frame, trm::direction dir, span const & calced_span)
{
	assert(m_pOwner);
	AsyncCall(*m_pOwner, &CXSegment::OnProcessingProgress, Span(calced_span), frame);
}

bool SegmentData::is_canceled() const
{
	return theApp.m_Processor.IsCanceled();
}

BEGIN_MESSAGE_MAP(CXSegment, My::SegmentCommands)
	ON_COMMAND(ID_SEGMENT_IMPORTMASK, OnImportKeyMask)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_IMPORTMASK, OnUpdateImportKeyMask)
	ON_COMMAND(ID_SEGMENT_EXPORTMASK, OnExportKeyMask)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_EXPORTMASK, OnUpdateExportKeyMask)
	ON_COMMAND(ID_SEGMENT_TRIM, OnTrim)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_TRIM, OnUpdateTrim)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_PROCESS, OnUpdateProcess) 
	ON_COMMAND(ID_SEGMENT_COPY_CONTOUR, OnCopyContour)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_COPY_CONTOUR, OnUpdateExportContour)
	ON_COMMAND(ID_SEGMENT_EXPORT_CONTOUR, OnExportContour)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_EXPORT_CONTOUR, OnUpdateExportContour)
	ON_COMMAND(ID_SEGMENT_RESET_PARAMETERS, OnResetParameters)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_RESET_PARAMETERS, OnUpdateResetParameters)
END_MESSAGE_MAP()

CXSegment::CXSegment(CXSegmentSet & s, int org) :
	m_Owner(s)
{
	Init(org);
	BitMask empty(m_Owner.GetComp().GetDim());
	empty.fill(false);
	DoSetKeyMask(empty);
}

CXSegment::CXSegment(CXSegmentSet & s, CXSegment const & Sample, int org) :
	m_Owner(s)
{
	Init(org);
	if (Sample.GetTimeSpan().Inside(org))
		m_TimeSpan = Sample.GetTimeSpan();
	DoSetKeyMask(Sample.GetBitMask(org));
	DoSetParams(Sample.GetParams());
}

void CXSegment::Init(int org)
{
	m_Origin = org;
	m_TimeSpan = Span(m_Origin, m_Origin + 1);
	m_DataSpan = Span(m_Origin, m_Origin); ///< empty
	m_Enqueued = false;
	m_MaskMode.mode = MaskMode::Substitute;
	m_MaskMode.inverted = false;
	m_pData.reset(new SegmentData(org, theComp().GetFootageSPtr(), this));

	Attach(&theApp.GetView());
	Attach(&m_Owner);
}

CXSegment::~CXSegment()
{
	CloseMaskEditor();
}

const CXComp & CXSegment::GetComp() const
{ 
	return m_Owner.GetComp(); 
}

CXComp & CXSegment::GetComp() 
{ 
	return m_Owner.GetComp(); 
}

CXSegmentSet & CXSegment::GetOwner()
{
	return m_Owner;
}

int CXSegment::GetOrigin() const
{
	return m_Origin;
}

Span CXSegment::GetTimeSpan() const
{
	return m_TimeSpan;
}

void CXSegment::SetTimeSpan(Span s)
{
	m_Owner.GetDoc().Perform(My::CreateChangeValue(* this,
		GetTimeSpan, DoSetTimeSpan,	s, "Adjust Segment"));
}

void CXSegment::DoSetTimeSpan(Span s)
{
	if (s != m_TimeSpan) {
		m_TimeSpan = s;
		UpdateProperty(&TimeSpan);
		DispatchEvent(TimeSpanChange(* this));
	}
}

MaskMode CXSegment::GetMaskMode() const
{
	return m_MaskMode;
}

void CXSegment::SetMaskMode(MaskMode s)
{
	m_Owner.GetDoc().Perform(My::CreateChangeValue(* this,
		GetMaskMode, DoSetMaskMode,	s, "Change Mask Mode of Segment"));
}

void CXSegment::DoSetMaskMode(MaskMode s)
{
	if (s != m_MaskMode) {
		m_MaskMode = s;
		UpdateProperty(&MaskModeProp);
		DispatchEvent(MaskModeChange(* this));
	}
}

Span CXSegment::GetDataSpan() const
{
	return m_DataSpan;
}

bool CXSegment::IsProcessed() const
{
	return m_Enqueued;
}

BitMask const & CXSegment::GetKeyMask() const
{
	return m_pData->key_mask();
}

bool CXSegment::HasKeyMask() const
{
	if (GetKeyMask().empty())
		return false;
	unsigned sc = GetKeyMask().set_bit_count();
	return sc != 0 && sc != GetKeyMask().size(); 
}

bool CXSegment::NeedsProcessing() const
{
	return CanStartProcessing();
}

bool CXSegment::StartProcessing()
{
	if (!CanStartProcessing())
		return false;

	m_Enqueued = true;
	m_pData->accessible_span_include(m_TimeSpan);
	DispatchEvent(StateChange(* this));

	CloseMaskEditor();
	theApp.m_Processor.Place(this);

	theApp.GetView().StartFollow();

	return true;
}

void CXSegment::CancelProcessing(bool Wait)
{
	if (!IsProcessed())
		return;

	theApp.m_Processor.Remove(this);
	if (!Wait || !IsProcessed())
		return;

	CDialog dlg;
	VERIFY(dlg.Create(IDD_STOPPING_CALCULATION, theApp.m_pMainWnd));
	dlg.CenterWindow();
	theApp.m_pMainWnd->EnableWindow(FALSE);
	dlg.EnableWindow(TRUE);
	dlg.ShowWindow(SW_SHOW);

	while (IsProcessed())
		theApp.m_Processor.IdleOrWndMsg(true);

	theApp.m_pMainWnd->EnableWindow(TRUE);
	VERIFY(dlg.DestroyWindow());
}

bool CXSegment::IsMaskEditing() const
{
	const My::MaskOwner * pThis = this;
	return GetMaskEditor().GetMaskOwner() == pThis;
}

void CXSegment::OpenMaskEditor()
{
	if (IsProcessed())
		return;
	CXSegment * pOld = static_cast<CXSegment *>(GetMaskEditor().GetMaskOwner());
	GetMaskEditor().Open(* this);
	DispatchEvent(StateChange(* this));
	if (pOld != 0)
		pOld->DispatchEvent(StateChange(* pOld));
}

void CXSegment::CloseMaskEditor()
{
	if (!IsMaskEditing())
		return;
	GetMaskEditor().Close();
	DispatchEvent(StateChange(* this));
}

void CXSegment::Release()
{
	CancelProcessing(true);
	CloseMaskEditor();
}

void CXSegment::SetDataSpan(Span DataSpan)
{
	if (m_DataSpan != DataSpan) {
		m_DataSpan = DataSpan;
		DispatchEvent(DataSpanChange(* this));
	}
}

void CXSegment::OnImportKeyMask()
{
	if (IsProcessed())
		return;
	
	CFileDialog dlg(TRUE, "bmp", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, 
		"Bitmap Files (*.bmp)|*.bmp||", AfxGetMainWnd());

	dlg.m_ofn.lpstrTitle = "Import Key Mask for the Segment"; 
	if (dlg.DoModal() != IDOK) 
		return;

	theApp.BeginActivity("importing mask");

	BitMask bmp;
	img::bmp::read(dlg.GetPathName(), bmp);
	unsigned sc = bmp.set_bit_count();
	if (sc == 0 || sc == bmp.width() * bmp.height()) {
		CString msg;
		msg.Format("Bitmap (%s) contains no %s pixels.\nProceed?", dlg.GetPathName(), 
			sc == 0 ? "white" : "black");
		if (AfxMessageBox(msg, MB_ICONEXCLAMATION|MB_OKCANCEL, 0) == IDCANCEL)
			return;
	}
	BitMask tmp;
	if (bmp.dim() != GetComp().GetDim()) {
		CString msg;
		msg.Format("Dimensions of the bitmap (%s) differ from composition dimensions.\nProceed?", dlg.GetPathName());
		if (AfxMessageBox(msg, MB_ICONEXCLAMATION|MB_OKCANCEL, 0) == IDCANCEL)
			return;
		tmp.swap(bmp);
		bmp.resize(GetComp().GetDim());
		bmp.fill(false);
		img::copy(bmp, tmp);
	}

	//GetComp().GetView().CancelWaitMergerIdle();
	SetKeyMask(bmp);

	theApp.EndActivity();
}

void CXSegment::SetKeyMask(BitMask const & m, const String & action)
{
	m_Owner.GetDoc().Perform(new SegmentMaskAction(* this, m, action));
}

void CXSegment::DoSetKeyMask(BitMask const & m)
{
	m_pData->key_mask(m);
	SetDataSpan(Span(m_Origin, m_Origin));
	if (IsMaskEditing())
		GetMaskEditor().UpdateBuffer();
}

const CXParams & CXSegment::GetParams() const
{
	return m_pData->param();
}

void CXSegment::SetParams(const CXParams & p)
{
	m_Owner.GetDoc().Perform(new SegmentParamsAction(* this, p));
}

bool CXSegment::DoSetParams(const CXParams & p)
{
	if (m_pData->param(p)) {
		SetDataSpan(m_pData->calced_masks_span());
		DispatchEvent(ParamsChange(*this));
		UpdateProperty(&Params);
		return true;
	}
	return false;
}

void CXSegment::OnUpdateImportKeyMask(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!IsProcessed());
}

void CXSegment::OnExportKeyMask()
{
	CFileDialog dlg(FALSE, "bmp", NULL, 
		OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT, 
		"Bitmap Files (*.bmp)|*.bmp||", AfxGetMainWnd());

	dlg.m_ofn.lpstrTitle = "Export Mask"; 
	if (dlg.DoModal() != IDOK) 
		return;

	theApp.BeginActivity("exporting mask");

	img::bmp::write1bit(dlg.GetPathName(), GetKeyMask());

	theApp.EndActivity();
}

void CXSegment::OnUpdateExportKeyMask(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(HasKeyMask());
}

void CXSegment::OnCopyContour()
{
	CWaitCursor wait;

	std::ostringstream stream;
	WriteContour(stream);
	My::ClipboardCopy(stream.str(), AfxGetMainWnd());
}

void CXSegment::OnExportContour()
{
	CFileDialog dlg(FALSE, "txt", NULL, 
		OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT, 
		"Contour Text Files (*.txt)|*.txt||", AfxGetMainWnd());

	dlg.m_ofn.lpstrTitle = "Export Contour"; 
	if (dlg.DoModal() != IDOK) 
		return;

	theApp.BeginActivity("exporting contour");

	std::ofstream f(dlg.GetPathName());
	f.exceptions(std::ios::badbit | std::ios::failbit);
	WriteContour(f);

	theApp.EndActivity();
}

void CXSegment::OnUpdateExportContour(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(GetParams().decrease_mask_freedom && !GetDataSpan().Empty());
}

void CXSegment::WriteContour(std::ostream & out)
{
	using namespace outline;

	out << "Animated Bezier Contour\n"
		"Framerate " << GetComp().GetExportFramerate() << '\n';
	Span Data = GetDataSpan();

	///collect all contours marked with their time in a vector
	std::vector< timed_points_f > Contours(Data.Size());
	int Frame;
	for (Frame = Data.first; Frame != Data.last; ++Frame) {
		assert (HasContour(Frame));
		Contours[Frame - Data.first].p = GetContour(Frame);
		Contours[Frame - Data.first].t = Frame;
	}

	typedef motion_line_estimator<timed_points_f> estimator_type;
	typedef simplifier<estimator_type> simplifier_type;
	simplifier_type::patch_vector patches;

	if (GetComp().GetExportInaccuracy() > 0) {
		simplifier<estimator_type> simplifier;
		simplifier.simplify_max_discrepancy(Contours.size() * Contours[0].size() * square(GetComp().GetExportInaccuracy()), Contours, patches);
		//insert last frame index
		patches.push_back(simplifier_type::patch()); 
		patches.back().istart = Data.Size() - 1;
	}
	else {
		patches.resize(Contours.size());
		for (Size i = 0; i < patches.size(); ++i)
			patches[i].istart = i;
	}

	for (Size i = 0; i < patches.size(); ++i) {
		timed_points_f const & Contour = Contours[patches[i].istart];
		out << "\nFrame " << Contour.t << '\n';
		for (Size n = 0; n < Contour.size(); ++n)
			out << Contour[n].p.x << '\t' << Contour[n].p.y << '\n';
	}
}

void CXSegment::DoTrimDataSpan()
{
	m_pData.reset(new SegmentData(*m_pData));
	if (m_TimeSpan.first > m_DataSpan.first)
		m_DataSpan.first = m_TimeSpan.first;
	if (m_TimeSpan.last < m_DataSpan.last)
		m_DataSpan.last = m_TimeSpan.last;
	m_pData->trim(m_DataSpan);
	DispatchEvent(DataSpanChange(* this)); //dispatch event even if previous data span was the same
}

void CXSegment::OnTrim()
{
	GetComp().Perform(new SegmentTrimAction(*this));
}

void CXSegment::OnUpdateTrim(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!IsProcessed() && IsTrimmable());
}

void CXSegment::OnResetParameters()
{
	SetParams(trm::default_param);
}

void CXSegment::OnUpdateResetParameters(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(GetParams() != trm::default_param);
}

void CXSegment::SetData(SegmentDataSPtr const & d)
{
	bool ParamsChanged = m_pData->param() != d->param();
	m_pData = d;
	SetDataSpan(m_pData->calced_masks_span());
	if (ParamsChanged) {
		DispatchEvent(ParamsChange(*this));
		UpdateProperty(&Params);
	}
	if (IsMaskEditing()) {
		//this call is required for undo/redo effect is displayed during mask editing
		theApp.GetView().InvalidateMask();
		GetMaskEditor().UpdateBuffer();
	}
}

xml::stream & CXSegment::Serialize(xml::stream & s)
{
	s || *m_pData;
	s.child("TimeSpan") || m_TimeSpan;
	s.child("MaskMode") || m_MaskMode;

	if (s.in()) {
		m_Origin = m_pData->origin();
		m_TimeSpan.Intersect(GetComp().GetTimeSpan());
		m_DataSpan = m_pData->calced_masks_span();
		Span s = m_DataSpan.Intersection(GetComp().GetTimeSpan());
		if (s != m_DataSpan) {
			m_DataSpan = s;
			m_pData->trim(s);
		}
		/* Pasha: KeyMask is moved back to the trm::segment,
		 *		  so we cannot help here :(
		if (m_KeyMask.dim() != GetComp().GetDim()) {
			BitMask tmp;
			tmp.swap(m_KeyMask);
			m_KeyMask.resize(GetComp().GetDim());
			m_KeyMask.fill(false);
			img::copy(m_KeyMask, tmp);
		}
		*/
		//remember that after loading and corection the segment may have been invalid, test it with Valid()
	}

	return s;
}

bool CXSegment::Valid() const
{
	return m_TimeSpan.Inside(m_Origin) &&
		GetKeyMask().dim() == GetComp().GetDim();
}

void CXSegment::OnProcessingProgress(Span DataSpan, int LastProcessedFrame) 
{ 
	GetComp().GetActions().Modified();
	theApp.GetView().OnFrameProcessed(this, LastProcessedFrame);
	SetDataSpan(DataSpan); 
}

void CXSegment::OnProcessingFinish()
{
	assert(m_Enqueued);
	m_Enqueued = false;
	SetDataSpan(m_pData->calced_masks_span());
	DispatchEvent(StateChange(* this));
	theApp.GetView().OnSegmentProcessed();
}

My::Sequence * CXSegment::GetSequence()
{
	return &GetComp();
}

// Properties

template <>
My::EnumItem<trm::mincut::information_source> My::EnumTypeControl<trm::mincut::information_source>::Items[] =
{
	{"Target Frame", trm::mincut::source_target},
	{"Both Frames", trm::mincut::source_both}
};
SPECIALIZE_ENUM_TYPE_CONTROL(trm::mincut::information_source)

template <>
My::EnumItem<refine::information_source> My::EnumTypeControl<refine::information_source>::Items[] =
{
	{"Reference Frame"/*ResourceString(IDS_ReferenceFrame).c_str()*/, refine::source_reference},
	{"Target Frame", refine::source_target},
	{"Both Frames", refine::source_both}
};
SPECIALIZE_ENUM_TYPE_CONTROL(refine::information_source)

template <>
My::EnumItem<refine::preference> My::EnumTypeControl<refine::preference>::Items[] =
{
	{"None", refine::prefer_none},
	{"Masked Pixels", refine::prefer_masked_pixels},
	{"Unmasked Pixels", refine::prefer_unmasked_pixels}
};
SPECIALIZE_ENUM_TYPE_CONTROL(refine::preference)

template <>
My::EnumItem<trm::smooth_mask_method> My::EnumTypeControl<trm::smooth_mask_method>::Items[] =
{
	{"Off", trm::smooth_mask_off},
	{"Simple", trm::smooth_mask_simple},
	{"Complex", trm::smooth_mask_complex},
	{"Diffuse", trm::smooth_mask_diffuse},
	//{"Method A", trm::smooth_mask_A},
	//{"Method B", trm::smooth_mask_B},
	//{"Method D", trm::smooth_mask_D},
	//{"Method E", trm::smooth_mask_E},
	//{"Method EA", trm::smooth_mask_EA},
	//{"Different Ellipses", trm::smooth_mask_different_ellipses}
};
SPECIALIZE_ENUM_TYPE_CONTROL(trm::smooth_mask_method)

enum Downsample { DownsampleOff = 1, Downsample2, Downsample3, Downsample4 };

template <>
My::EnumItem<Downsample> My::EnumTypeControl<Downsample>::Items[] =
{
	{"Off", DownsampleOff},
	{"2x2 -> 1", Downsample2},
	{"3x3 -> 1", Downsample3},
	{"4x4 -> 1", Downsample4}
};
SPECIALIZE_ENUM_TYPE_CONTROL(Downsample)

// Example of showing boolean values as drop-down list
enum DecisionThresholdEnum { DecisionThresholdManual, DecisionThresholdAuto };
struct DecisionThreshold {
	DecisionThresholdEnum v;
	DecisionThreshold() { }
	DecisionThreshold(DecisionThresholdEnum vv) { v = vv; }
	DecisionThreshold(bool b) { v = (b ? DecisionThresholdAuto : DecisionThresholdManual); }
	operator bool() const { return v == DecisionThresholdAuto; }
};

template <>
My::EnumItem<DecisionThreshold> My::EnumTypeControl<DecisionThreshold>::Items[] =
{
	{"Manual", DecisionThresholdManual},
	{"Auto", DecisionThresholdAuto}
};
SPECIALIZE_ENUM_TYPE_CONTROL(DecisionThreshold) 

My::Property<Span>		CXSegment::TimeSpan;
My::Property<MaskMode>	CXSegment::MaskModeProp;
My::Property<CXParams>	CXSegment::Params;

void CXSegment::InitPropertyMap(My::PropertyMap & map)
{
	map[IDS_SEGMENT_MASK] = &MaskModeProp.Read(GetMaskMode).Write(SetMaskMode).Hint(IDS_SEGMENT_MASK_HINT);
	map[IDS_TRACKING_BOUNDS] = &TimeSpan.Read(GetTimeSpan).Write(SetTimeSpan).Hint(IDS_HINT_TRACKING_BOUNDS);
	map[IDS_PARAMETERS] = &Params.Read(GetParams).Write(SetParams);
}

My::Property<CXParams>::Property()
	: StructProperty<CXParams>(true)
{
}

void My::Property<CXParams>::Init()
{
	(* this)[IDS_ReferenceFramePitch] = &Create(&CXParams::reference_frame_pitch).Hint(IDS_TOOLTIP_ReferenceFramePitch);
	(* this)[IDS_Downsample] = //&Create(&CXParams::downsample);
		&Create(ReadWriteAs<Downsample>(* this, &CXParams::downsample)).Hint(IDS_TOOLTIP_Downsample);
	static GroupProperty<bool> OpticFlow;
	(* this)[IDS_OpticFlow] = &OpticFlow.ReadWrite(* this, &CXParams::optic_flow).Hint(IDS_TOOLTIP_OpticFlow);
	{
		ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::optic_flow));
		OpticFlow[IDS_MaxMotion] = &Create(&CXParams::max_motion).ReadOnly(pReadOnly).Hint(IDS_TOOLTIP_MaxMotion);
		OpticFlow[IDS_TrackerSize] = &Create(&CXParams::tracker_size).ReadOnly(pReadOnly).Hint(IDS_TOOLTIP_TrackerSize);
	}
	static GroupProperty<bool> MinCut;
	(* this)[IDS_MINCUT] = &MinCut.ReadWrite(* this, &CXParams::mincut).Hint(IDS_MINCUT_HINT);
	{
		ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::mincut));
		//static GroupProperty<int> Pyramid;
		MinCut[IDS_MINCUT_PYRAMID_LEVELS] = //&Pyramid.ReadWrite(* this, &CXParams::mincut_pyramid_levels)
			&Create(&CXParams::mincut_pyramid_levels).ReadOnly(pReadOnly).Hint(IDS_MINCUT_PYRAMID_LEVELS_HINT);
		//{
		//	ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::mincut_pyramid));
		//	Pyramid[IDS_MINCUT_PYRAMID_OVERWORK] = &Create(&CXParams::mincut_pyramid_overwork).ReadOnly(pReadOnly).Hint(IDS_MINCUT_PYRAMID_OVERWORK_HINT);
		//}
		MinCut[IDS_MINCUT_MAX_MOTION] = &Create(&CXParams::mincut_max_motion).ReadOnly(pReadOnly).Hint(IDS_MINCUT_MAX_MOTION_HINT);
		MinCut[IDS_MINCUT_INFORMATION_SOURCE] = &Create(&CXParams::mincut_information_source).ReadOnly(pReadOnly).Hint(IDS_MINCUT_INFORMATION_SOURCE_HINT);
		MinCut[IDS_MINCUT_MAX_LINK_DISTANCE] = &Create(&CXParams::mincut_max_link_distance).ReadOnly(pReadOnly).Hint(IDS_MINCUT_MAX_LINK_DISTANCE_HINT);
		MinCut[IDS_MINCUT_LINK_COUNT] = &Create(&CXParams::mincut_link_count).ReadOnly(pReadOnly).Hint(IDS_MINCUT_LINK_COUNT_HINT);
		MinCut[IDS_MINCUT_ASPECT] = &Create(&CXParams::mincut_aspect).ReadOnly(pReadOnly).Hint(IDS_MINCUT_ASPECT_HINT);
		ReadBoolSPtr pInterframeMinCut = Not(My::Read<bool>(* this, &CXParams::interframe_mincut));
		MinCut[IDS_MINCUT_INTERFRAME_MAX_LINK_DISTANCE] = &Create(&CXParams::mincut_interframe_max_link_distance).ReadOnly(pInterframeMinCut).Hint(IDS_MINCUT_INTERFRAME_MAX_LINK_DISTANCE_HINT);
		MinCut[IDS_MINCUT_INTERFRAME_LINK_COUNT] = &Create(&CXParams::mincut_interframe_link_count).ReadOnly(pInterframeMinCut).Hint(IDS_MINCUT_INTERFRAME_LINK_COUNT_HINT);

		static GroupProperty<bool> Antialising;
		ReadBoolSPtr pDisallowAntialising = Or(pReadOnly, Not(My::Read<bool>(* this, &CXParams::smooth_off)));
		MinCut[IDS_MINCUT_ANTIALIASING] = &Antialising.ReadWrite(* this, &CXParams::mincut_antialiasing).ReadOnly(pDisallowAntialising).Hint(IDS_MINCUT_ANTIALIASING_HINT);
		{
			ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::mincut_and_antialiasing));
			Antialising[IDS_MINCUT_ANTIALIASING_LEVELS_EXP] = &Create(&CXParams::mincut_antialiasing_levels_exp).ReadOnly(pReadOnly).Hint(IDS_MINCUT_ANTIALIASING_LEVELS_EXP_HINT);
		}
	}
	/*static GroupProperty<bool> Entropic;
	(* this)[IDS_ENTROPIC_TRACKING] = &Entropic.ReadWrite(* this, &CXParams::entropic_tracker).Hint(IDS_ENTROPIC_TRACKING_HINT);
	{
		ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::entropic_tracker));
		Entropic[IDS_ENTROPIC_BEST_MOVE_ITER_NUM] = &Create(&CXParams::entropic_best_move_iter_num).ReadOnly(pReadOnly).Hint(IDS_ENTROPIC_BEST_MOVE_ITER_NUM_HINT);
		Entropic[IDS_ENTROPIC_MOVE_RADIUS] = &Create(&CXParams::entropic_move_radius).ReadOnly(pReadOnly).Hint(IDS_ENTROPIC_MOVE_RADIUS_HINT);
		Entropic[IDS_ENTROPIC_BAND_RADIUS] = &Create(&CXParams::entropic_band_radius).ReadOnly(pReadOnly).Hint(IDS_ENTROPIC_BAND_RADIUS_HINT);
		Entropic[IDS_ENTROPIC_UNAMBIG_THR] = &Create(&CXParams::entropic_unambig_thr).ReadOnly(pReadOnly).Hint(IDS_ENTROPIC_UNAMBIG_THR_HINT);
		Entropic[IDS_ENTROPIC_ALPHA] = &Create(&CXParams::entropic_alpha).ReadOnly(pReadOnly).Hint(IDS_ENTROPIC_ALPHA_HINT);
		Entropic[IDS_ENTROPIC_SQRT_CONTROL] = &Create(&CXParams::entropic_sqrt_control).ReadOnly(pReadOnly).Hint(IDS_ENTROPIC_SQRT_CONTROL_HINT);
		Entropic[IDS_ENTROPIC_SAFE_TIME_STEP] = &Create(&CXParams::entropic_safe_time_step).ReadOnly(pReadOnly).Hint(IDS_ENTROPIC_SAFE_TIME_STEP_HINT);
	}*/
	static GroupProperty<bool> Refine;
	(* this)[IDS_Refine] = &Refine.ReadWrite(* this, &CXParams::refine_mask).Hint(IDS_TOOLTIP_Refine);
	{
		ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::refine_mask));
		Refine[IDS_InformationSource] = &Create(&CXParams::refine_information_source).ReadOnly(pReadOnly).Hint(IDS_TOOLTIP_RefineInformationSource);
		Refine[IDS_WindowHalfSize] = &Create(&CXParams::refine_window_half_size).ReadOnly(pReadOnly).Hint(IDS_TOOLTIP_RefineWindowHalfSize);
		//Refine[IDS_Predominance] = &Create(&CXParams::refine_predominance).ReadOnly(pReadOnly).Hint(IDS_TOOLTIP_RefinePredominance);
		//Refine[IDS_EnoughStatistics] = &Create(&CXParams::refine_enough_statistics).ReadOnly(pReadOnly).Hint(IDS_TOOLTIP_RefineEnoughStatistics);
		//Refine[IDS_REFINE_PREFER] = &Create(&CXParams::refine_preference).ReadOnly(pReadOnly).Hint(IDS_REFINE_PREFER_HINT);
		//Refine["Sort by"] = 
		//	&Create(ReadWriteAs<SortBy>(* this, &CXParams::refine_multiply_color_space)).ReadOnly(pReadOnly);

		static GroupProperty<DecisionThreshold> DecisThrs;
		Refine[IDS_REFINE_DECISION_THRS] = &DecisThrs.ReadWrite(
			ReadWriteAs<DecisionThreshold>(* this, &CXParams::refine_auto)); 
		{
			ReadBoolSPtr pDetailsReadOnly = My::Or(pReadOnly, My::Read<bool>(* this, &CXParams::refine_auto));
			DecisThrs[IDS_REFINE_T] = &Create(&CXParams::refine_enough_statistics).ReadOnly(pDetailsReadOnly).Hint(IDS_REFINE_T_HINT);
			DecisThrs[IDS_REFINE_PREFER] = &Create(&CXParams::refine_preference).ReadOnly(pDetailsReadOnly).Hint(IDS_REFINE_PREFER_HINT);
		}
	}
	static GroupProperty<bool> Refine2;
	(* this)[IDS_REFINE2] = &Refine2.ReadWrite(* this, &CXParams::refine2_mask);
	{
		ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::refine2_mask));
		Refine2[IDS_REFINE2_MAX_MOTION] = &Create(&CXParams::refine2_max_motion).ReadOnly(pReadOnly).Hint(IDS_REFINE2_MAX_MOTION_HINT);
		Refine2[IDS_REFINE2_MAX_LEVEL] = &Create(&CXParams::refine2_max_level).ReadOnly(pReadOnly).Hint(IDS_REFINE2_MAX_LEVEL_HINT);
	}
	/*static GroupProperty<bool> Refine3;
	(* this)[IDS_REFINE3] = &Refine3.ReadWrite(* this, &CXParams::refine3_mask);
	{
		ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::refine3_mask));
		Refine3[IDS_InformationSource] = &Create(&CXParams::refine3_information_source).ReadOnly(pReadOnly).Hint(IDS_TOOLTIP_RefineInformationSource);
		Refine3[IDS_REFINE_DECISION_THRS] = &Create(&CXParams::refine3_enough_statistics).ReadOnly(pReadOnly).Hint(IDS_REFINE_T_HINT);
		Refine3[IDS_REFINE_PREFER] = &Create(&CXParams::refine3_preference).ReadOnly(pReadOnly).Hint(IDS_REFINE_PREFER_HINT);
	}*/
	(* this)[IDS_DILATE_ERODE] = &Create(&CXParams::dilation_erosion).Hint(IDS_DILATE_ERODE_HINT);
	(* this)[IDS_WOOL_REMOVAL] = &Create(&CXParams::wool_removal).Hint(IDS_WOOL_REMOVAL_HINT);
	static GroupProperty<bool> DecreaseMaskFreedom;
	(* this)[IDS_DECREASE_MASK_FREEDOM] = &DecreaseMaskFreedom.ReadWrite(* this, &CXParams::decrease_mask_freedom).Hint(IDS_HINT_DECREASE_MASK_FREEDOM);
	{
		ReadBoolSPtr pReadOnly = Not(My::Read<bool>(* this, &CXParams::decrease_mask_freedom));
		DecreaseMaskFreedom[IDS_DECREASE_FREEDOM_DEGREES] = &Create(&CXParams::decrease_freedom_degrees).ReadOnly(pReadOnly).Hint(IDS_HINT_DECREASE_FREEDOM_DEGREES);
		DecreaseMaskFreedom[IDS_DECREASE_SMOOTH_CONTOUR] = &Create(&CXParams::decrease_smooth_contour).ReadOnly(pReadOnly).Hint(IDS_HINT_DECREASE_SMOOTH_CONTOUR);
		DecreaseMaskFreedom[IDS_DECREASE_ITERATIONS] = &Create(&CXParams::decrease_iterations).ReadOnly(pReadOnly).Hint(IDS_HINT_DECREASE_ITERATIONS);
		DecreaseMaskFreedom[IDS_DECREASE_NODES_INERTIA] = &Create(ReadWriteAs< My::Percent<2> >(* this, &CXParams::decrease_nodes_traction)).ReadOnly(pReadOnly).Hint(IDS_HINT_DECREASE_NODES_INERTIA);
		DecreaseMaskFreedom[IDS_DECREASE_TANGENTS_INERTIA] = &Create(ReadWriteAs< My::Percent<2> >(* this, &CXParams::decrease_tangents_traction)).ReadOnly(pReadOnly).Hint(IDS_HINT_DECREASE_TANGENTS_INERTIA);
	}
	static GroupProperty<trm::smooth_mask_method> SmoothMask;
	(* this)[IDS_SMOOTH_MASK] = &SmoothMask.ReadWrite(* this, &CXParams::smooth_mask).Hint(IDS_TOOLTIP_SmoothMask);
	{
		ReadBoolSPtr pReadOnly = My::Read<bool>(* this, &CXParams::smooth_off);
		//ReadBoolSPtr pSmoothB = My::Read<bool>(* this, &CXParams::smooth_b);
		//ReadBoolSPtr pShowBoldness = My::Read<bool>(* this, &CXParams::smooth_depend_on_max_condition_number);
		//ReadBoolSPtr pHasDepth = My::Read<bool>(* this, &CXParams::smooth_has_depth);
		//SmoothMask[IDS_SMOOTH_FORE_DEPTH] = &Create(&CXParams::smooth_fore_depth).ReadOnly(pReadOnly).Visible(pHasDepth).Hint(IDS_SMOOTH_FORE_DEPTH_HINT);
		//SmoothMask[IDS_SMOOTH_BACK_DEPTH] = &Create(&CXParams::smooth_back_depth).ReadOnly(pReadOnly).Visible(pHasDepth).Hint(IDS_SMOOTH_BACK_DEPTH_HINT);
		SmoothMask[IDS_SMOOTH_WHS] = &Create(&CXParams::smooth_window_half_size).ReadOnly(pReadOnly).Hint(IDS_SMOOTH_WHS_HINT);
		SmoothMask[IDS_SMOOTH_MAX_CONDITION_NUMBER] = &Create(&CXParams::smooth_max_condition_number).ReadOnly(pReadOnly)./*Visible(pShowBoldness).*/Hint(IDS_SMOOTH_MAX_CONDITION_NUMBER_HINT);
		SmoothMask[IDS_SMOOTH_PASSES_NUM] = &Create(&CXParams::smooth_passes_num).ReadOnly(pReadOnly).Hint(IDS_SMOOTH_PASSES_NUM_HINT);
		//SmoothMask[IDS_SMOOTH_SHARPNESS] = &Create(ReadWriteAs< My::Percent<2> >(* this, &CXParams::smooth_sharpness)).ReadOnly(pReadOnly).Visible(pSmoothB).Hint(IDS_SMOOTH_SHARPNESS_HINT);
		//SmoothMask[IDS_SMOOTH_NOISINESS] = &Create(&CXParams::smooth_noisiness).ReadOnly(pReadOnly).Visible(pSmoothB).Hint(IDS_SMOOTH_NOISINESS_HINT);
		SmoothMask[IDS_SMOOTH_DECORATIVE] = &Create(&CXParams::smooth_decorative).ReadOnly(pReadOnly).Hint(IDS_SMOOTH_DECORATIVE_HINT);
	}
}
